1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.api.data.tilemap; 12 public import hip.api.renderer.texture; 13 public import hip.util.variant; 14 /** 15 * TileLayers representations which can be found from Tiled. 16 */ 17 enum TileLayerType 18 { 19 ///The Default one, a data structure holding tile mapping information. 20 TILE_LAYER = "tilelayer", 21 ///A simple layer, containing only an image to be rendered above or below the tile layers. 22 IMAGE_LAYER = "imagelayer", 23 ///A layer used only to hold data to be used within the gameplay implementation 24 OBJECT_LAYER = "objectgroup" 25 } 26 27 enum TileDrawOrder : ubyte 28 { 29 TOP_DOWN, 30 DOWN_TOP 31 } 32 33 /** 34 * Simple Key/Value type defining a property inside a Tile. 35 */ 36 struct TileProperty 37 { 38 string type; 39 Variant val; 40 pragma(inline, true) T get(T)() 41 { 42 static if(is(T == string)) return val.get!string; 43 else 44 { 45 if(val.type == Type.string_) 46 val = Variant.make!T(val.get!string); 47 return val.get!T; 48 } 49 } 50 pragma(inline, true) T set(T)(T v) => val.set(v); 51 } 52 53 enum TiledObjectTypes : ubyte 54 { 55 rect, 56 ellipse, 57 tile, 58 point, 59 text, 60 line, 61 triangle, 62 polygon 63 } 64 65 66 /** 67 * A simple object which can mean absolutely anything inside a TileLayer. 68 * Usually used for implementing: 69 - Camera Dead Zones 70 - Event Systems 71 - Trigger Areas 72 73 When wanting to get its following type, call either `rect`, `ellipse`, `tile`, `point`, `line`, `triangle`, `polygon` or `text`. Try to avoid calling 74 `text` multiple times since it involves 3 associative array accesses. 75 */ 76 struct TiledObject 77 { 78 ushort id; 79 bool visible; 80 TiledObjectTypes dataType; 81 string name; 82 string type; 83 84 TiledObjectUnion data; 85 TileProperty[string] properties; 86 87 ref TiledRectangle rect() @trusted @nogc { assert(dataType == TiledObjectTypes.rect); return data.rect;} 88 ref TiledRectangle ellipse() @trusted @nogc { assert(dataType == TiledObjectTypes.ellipse); return data.rect;} 89 ref TiledRectangle tile() @trusted @nogc { assert(dataType == TiledObjectTypes.tile); return data.rect;} 90 ref int[2] point() @trusted @nogc { assert(dataType == TiledObjectTypes.point); return data.rect.getPoint();} 91 ref int[4] line() @trusted @nogc { assert(dataType == TiledObjectTypes.line); return data.rect.getLine();} 92 ref int[2][3] triangle() @trusted @nogc { assert(dataType == TiledObjectTypes.triangle); return data.triangle;} 93 ref int[2][] polygon() @trusted @nogc { assert(dataType == TiledObjectTypes.polygon); return data.polygon;} 94 95 const(TiledText) text() @trusted 96 { 97 assert(dataType == TiledObjectTypes.text); 98 TileProperty* ff = "__fontFamily" in properties; 99 TileProperty* msg = "__text" in properties; 100 TileProperty* ww = "__wordWrap" in properties; 101 102 return TiledText(data.rect, msg? msg.get!string : null, ff ? ff.get!string : null, ww ? ww.get!bool : false); 103 } 104 } 105 106 struct TiledText 107 { 108 TiledRectangle rect; 109 string text; 110 string fontFamily; 111 bool wordWrap; 112 113 alias rect this; 114 } 115 struct TiledRectangle 116 { 117 int x, y, width, height; 118 ushort rotation, gid; 119 ref int[2] getPoint() @trusted @nogc { return (cast(int*)&this)[0..2]; } 120 ref int[4] getLine() @trusted @nogc { return (cast(int*)&this)[0..4]; } 121 } 122 123 union TiledObjectUnion 124 { 125 TiledRectangle rect; 126 int[2][3] triangle; 127 int[2][] polygon; 128 } 129 130 131 /** 132 * The TileAnimationFrame holds what is the ID of the current frame in a TileAnimation and how much duration. 133 */ 134 struct TileAnimationFrame 135 { 136 ushort id; 137 int duration; 138 } 139 140 /** 141 * The Tile is the smallest piece of a TileMap. 142 */ 143 struct Tile 144 { 145 ///ID for backreferencing from TileMap 146 ushort id; 147 ///Which frame is in its animatoin 148 ushort currentFrame; 149 ///The tile texture region itself. 150 IHipTextureRegion region; 151 /** 152 * Properties about the tile. Usually used for implementing gameplay stats such as: 153 - Sounds 154 - Collisions and Collidibles 155 - Visual Effects 156 */ 157 TileProperty[string] properties; 158 ///Frames for playing the tile animation 159 TileAnimationFrame[] animation; 160 alias properties this; 161 } 162 163 /** 164 * Tile Layer is a map of tiles consisting in a unique "texture" of tiles. 165 */ 166 final class HipTileLayer 167 { 168 ///Layer name on the tilemap editor 169 string name; 170 ///Data 171 ushort[] tiles; 172 bool visible = true; 173 int x, y, width, height; 174 uint tileWidth, tileHeight; 175 ushort id; 176 string type; 177 string drawOrder; 178 TileProperty[string] properties; 179 TiledObject[] objects; 180 float opacity = 1.0; 181 182 this(IHipTilemap map) 183 { 184 tileWidth = map.tileWidth; 185 tileHeight = map.tileHeight; 186 } 187 188 /** 189 * 190 */ 191 this(string name, uint width, uint height, ushort id, IHipTilemap map) 192 { 193 this(map); 194 this.name = name; 195 this.id = id; 196 this.width = width; 197 this.height = height; 198 tiles = new ushort[width*height]; 199 } 200 201 bool isInLayerBoundaries(int x, int y, int w, int h) const @nogc 202 { 203 int x2 = x+w; 204 int y2 = y+h; 205 206 bool notInBoundariesX = x2 < this.x || x > this.width + this.x; 207 if(notInBoundariesX) 208 return false; 209 bool notInBoundariesY = y2 < this.y || y > this.height + this.y; 210 211 return !notInBoundariesY; 212 } 213 214 ///Expects I and J in column/row 215 pragma(inline, true) 216 ushort getTile(uint i, uint j) @nogc @safe 217 { 218 return tiles[j*width+i]; 219 } 220 ushort checkedGetTile(uint i, uint j) @nogc @trusted 221 { 222 int target = j*width+i; 223 if(i >= width || j >= height || target < 0 || target >= tiles.length) 224 return 0; 225 return tiles.ptr[target]; 226 } 227 228 ///Gets tile from relative X and Y. Does not take into account the layer x, y 229 ushort getTileXY(uint x, uint y) @nogc @safe 230 { 231 return getTile(cast(uint)(x / tileWidth), cast(uint)(y / tileHeight)); 232 } 233 234 ///Gets tile from absolute X and Y. Takes into account the layer x, y 235 ushort checkedGetTileXY(int x, int y) @nogc @trusted 236 { 237 if(x < this.x || y < this.y || x > this.x+this.width || y > this.y+this.height) 238 return 0; 239 y-= this.y; 240 x-= this.x; 241 return checkedGetTile(x / tileWidth, y / tileHeight); 242 } 243 244 } 245 246 /** 247 * Tileset is a data set containing tiles. It has information about the underlying texture used for a tile. 248 */ 249 interface IHipTileset 250 { 251 uint columns() const; 252 253 ///Means where the tileset id starts 254 uint firstGid() const; 255 256 257 ///"image" in tiled 258 259 string texturePath() const; 260 ///"imageheight" in tiled 261 uint textureHeight() const; 262 ///"imagewidth" in tiled 263 uint textureWidth() const; 264 265 IHipTexture texture(); 266 int margin() const; 267 string name() const; 268 269 ///Only available when loaded via .tsx 270 string path() const; 271 int spacing() const; 272 uint tileHeight() const; 273 uint tileWidth() const; 274 275 ///Usually only accessed when looking for a specific property 276 Tile[] tiles(); 277 278 final uint tileCount()const {return cast(uint)(cast(IHipTileset)this).tiles.length;} 279 final IHipTextureRegion getTextureRegion(ushort id) 280 { 281 return tiles[id - firstGid].region; 282 } 283 final Tile* getTile(ushort id){return &tiles[id - firstGid];} 284 } 285 286 287 /** 288 * Tilemap is a set of tile layers. It contains information on the maximum map size, holds all the tilesets needed for 289 * actually drawing the layers. 290 */ 291 interface IHipTilemap 292 { 293 @nogc ref int x(); 294 @nogc ref int y(); 295 @nogc ref HipColor color(); 296 @nogc ref float scaleX(); 297 @nogc ref float scaleY(); 298 299 ///Returns scaleX as the one to be modified. 300 @nogc float scale(); 301 ///Modifies both scaleX and scaleY at the same time. 302 @nogc float scale(float v); 303 ref float rotation(); 304 305 306 string path() const; 307 uint width() const @nogc; 308 uint height() const @nogc; 309 bool isInfinite() const @nogc; 310 ref HipTileLayer[string] layers(); 311 string orientation() const @nogc; 312 string renderorder() const @nogc; 313 string tiled_version() const @nogc; 314 315 uint tileHeight() const @nogc; 316 uint tileWidth() const @nogc; 317 318 final uint tileWidthScaled() @nogc {return cast(uint)(scaleX * tileWidth);} 319 final uint tileHeightScaled() @nogc {return cast(uint)(scaleY * tileHeight);} 320 321 void setTileSize(uint tileWidth, uint tileHeight); 322 ///Use it when programatically creating your tilemap 323 void addTileset(IHipTileset tileset); 324 ///Use it when programatically creating your tilemap 325 final HipTileLayer addNewLayer(string layerName, uint columns, uint rows) 326 { 327 return layers[layerName] = new HipTileLayer(layerName, columns, rows, cast(ushort)layers.length, this); 328 } 329 330 331 IHipTileset getTilesetForID(ushort id); 332 final IHipTextureRegion getTextureRegionForID(ushort id){return getTilesetForID(id).getTextureRegion(id);} 333 final Tile* getTileForID(ushort id){return getTilesetForID(id).getTile(id);} 334 335 336 alias layers this; 337 }